home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastp9a.asm < prev    next >
Assembly Source File  |  1995-01-01  |  17KB  |  444 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / stereo / 4..44khz
  9. ; Parameters  : none
  10. ; Note        : Before compiling this call first "VOC2RAW TEST1.VOC /I /R"
  11. ;         to creat "TEST1.INC" in this case a file with 8bit mono signed data
  12. ;
  13. ; ■ DSP command 41h  ... set sample rate
  14. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  15. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  16. ; ■ DSP command C6h 020h ... autoinit 8 bit stereo data with no sign
  17. ;
  18.  
  19. .MODEL small
  20. .286
  21. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  22.  
  23. ; SoundBlaster SETUP
  24. BASEADDR           EQU 0220h       ;SoundBlaster base address
  25. IRQ7               EQU 15          ;SoundBlaster IRQ
  26. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  27.  
  28. ; PIC MASKS FOR MASK/DEMASK IRQ
  29. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  30. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  31.  
  32. ; DMA CONTROLLER REGISTERS :
  33. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  34. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  35. CLEARFLIPFLOP      EQU 00ch
  36. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  37. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMAchannel 1
  38. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  39.  
  40. ; SAMPLERATE :
  41. RATE               EQU 02AEDh       ; = 10989 Hz
  42.  
  43. ; DMA WRITE MODE
  44. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  45.  
  46. ;──────────────────────────────────────────────────────────────────────────────
  47. ; MACRO DEFINITIONs
  48. ;──────────────────────────────────────────────────────────────────────────────
  49. STARTUP                 MACRO
  50. ; MASM 5.x COMPATIBILITY
  51. __start:                mov     ax,DGROUP
  52.                         mov     ds,ax
  53.                         mov     bx,ss
  54.                         sub     bx,ax
  55.                         shl     bx,004h
  56.                         mov     ss,ax
  57.                         add     sp,bx
  58. ENDM
  59.  
  60. WAITWRITE               MACRO
  61. LOCAL                   loopWait,endloop
  62. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  63. ;          Returns   : n/a
  64. ;          Destroys  : AL
  65.  
  66.                         push    cx
  67.                         xor     cx,cx           ; need that for slow SBs !
  68. loopWait:               dec     cx
  69.                         jz      endloop
  70.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  71.                         or      al,al
  72.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  73. endloop:                pop     cx
  74. ENDM
  75.  
  76. WAITREAD                MACRO
  77. LOCAL                   loopWait,endloop
  78. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  79. ;          Returns   : n/a
  80. ;          Destroys  : AL
  81.  
  82.                         push    cx
  83.                         xor     cx,cx           ; need that for slow SBs !
  84. loopWait:               dec     cx
  85.                         jz      endloop
  86.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  87.                         or      al,al
  88.                         jns     loopWait        ; Jump if bit7=0 - no data available
  89. endloop:                pop     cx
  90. ENDM
  91.  
  92. RESET_DSP               MACRO
  93. local                   SBthere
  94. ;          Arguments : n/a
  95. ;          Returns   : n/a
  96. ;          Destroys  : DX,AL
  97.  
  98.                         mov      dx,BASEADDR+06h
  99.                         mov      al,1
  100.                         out      dx,al          ; start DSP reset
  101.  
  102.                         in       al,dx
  103.                         in       al,dx
  104.                         in       al,dx
  105.                         in       al,dx          ; wait 3 µsec
  106.  
  107.                         xor      al,al
  108.                         out      dx,al          ; end DSP Reset
  109.  
  110.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  111.                         WAITREAD
  112.                         sub      dx,4           ; dx = DSP Read Data
  113.                         in       al,dx
  114.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  115.                         je       SBthere
  116.                         jmp      RESET_ERROR    ; No SB - exit program
  117. SBthere:
  118. ENDM
  119. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  120.  
  121. .STACK 100h
  122.  
  123. .DATA
  124. ;──────────────────────────────────────────────────────────────────────────────
  125. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I /R" or creat your own
  126. ; textfile with sampledata
  127.  
  128. SAMPLEBUFFER LABEL BYTE
  129.     INCLUDE TEST1.INC
  130. SAMPLEBUFFEREND LABEL BYTE
  131.  
  132.     PART                db 1
  133.  
  134.     information         db 13,10,'DMASTP9A.EXE - play 8bit unsign stereo data (only SB16/SB16ASP)'
  135.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  136.                         db 13,10,'Stop playing with <ESC>.','$'
  137.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  138.     txtpart0            db 13,10,'playing part 0','$'
  139.     txtpart1            db 13,10,'playing part 1','$'
  140.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  141.  
  142.     OLDInterruptSEG     dw ?
  143.     OLDInterruptOFS     dw ?
  144.  
  145.     DMAbufferOFS        dw ?
  146.     DMAbufferPage       db ?
  147.  
  148.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  149. ;──────────────────────────────────────────────────────────────────────────────
  150. .CODE
  151.  STARTUP
  152.  
  153.            ; FIRST FREE NOT USED MEMORY :
  154.            mov     bx,ss
  155.            mov     ax,es
  156.            sub     bx,ax
  157.  
  158.            mov     ax,sp
  159.            add     ax,15
  160.  
  161.            shr     ax,4
  162.  
  163.            add     bx,ax
  164.            mov     ah,04ah
  165.            int     21h
  166.  
  167.            ; NOW ALLOCATE DMABUFFER
  168.            mov     bx,SAMPLEBufferlength+15
  169.            shr     bx,3            ; cx = samplebufferlength*2/16
  170.                                    ; (count of 16byte blocks)
  171.            mov     ah,48h
  172.            int     21h
  173.            jnc     enoughmem       ; ok got the memory
  174.            mov     dx,offset memerror
  175.            mov     ah,9
  176.            int     21h             ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  177.            jmp     return2dos
  178. enoughmem: ; AX = segment of DMA buffer / offset = 0
  179.  
  180. ;──────────────────────────────────────────────────────────────────────────────
  181. ; calculate page and offset for DMAcontroller :
  182. ;
  183. ; segment*16+offset = 20bit memory location -> upper 4 bits  = page
  184. ;                                              lower 16 bits = offset
  185. ;──────────────────────────────────────────────────────────────────────────────
  186.            rol     ax,4
  187.            mov     bl,al
  188.            and     bl,00fh
  189.            and     al,0f0h
  190.            mov     [DMABufferOFS],ax
  191.            mov     [DMAbufferPage],bl
  192. ;──────────────────────────────────────────────────────────────────────────────
  193. ; check for DMApage override :
  194. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  195. ; transfer data is placed in one page - no page overrides are allowed
  196. ;──────────────────────────────────────────────────────────────────────────────
  197. ; To solve that :
  198. ; creat a DMA buffer with double size you want - if the first part is placed
  199. ; on a page border the second part is for sure not
  200. ;──────────────────────────────────────────────────────────────────────────────
  201.            mov     cx,SAMPLEBUFFERLENGTH
  202.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  203.            cmp     ax,cx
  204.            ja      nooverride
  205.  
  206.            ; USE SECOND PART :
  207.            neg     ax               ; ax = offset first data
  208.            add     ax,cx            ; use second part
  209.            inc     ax             ; cx+1 bytes to next part ;)
  210.            mov     [DMABufferOFS],ax
  211.            inc     [DMABufferPage]  ; 2nd part is on next page !
  212. nooverride:
  213.  
  214. ;──────────────────────────────────────────────────────────────────────────────
  215. ; Now move the sampledata into the DMAbuffer
  216. ; in a MODplayer you'll do that in the IRQ,
  217. ; but in our case we only repeat the same sound in the DMAbuffer
  218. ;──────────────────────────────────────────────────────────────────────────────
  219.            ; FIRST CALCULATE THE DOS SEG/OFS FROM DMAPage/OFS
  220.            mov     al,byte ptr [DMABufferOFS]
  221.            and     al,0fh
  222.            xor     ah,ah
  223.            mov     di,ax       ; di = offset of DMAbuffer
  224.            mov     ax,[DMABufferOFS]
  225.            and     al,0f0h
  226.            or      al,[DMABufferPage]
  227.            ror     ax,4
  228.            mov     es,ax       ; es = segment of DMABuffer
  229.            mov     si,offset samplebuffer
  230.  
  231.            ; DS:SI - samples in dataseg
  232.            ; ES:DI - DMABuffer
  233.            ; CX - count of samples
  234.            ;
  235.            rep movsb
  236.  
  237.            RESET_DSP
  238.  
  239.            ; WRITE INFOMRATION TO SCREEN :
  240.            mov     dx,offset information
  241.            mov     ah,9
  242.            int     21h                  ; write program information to screen
  243.  
  244.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  245.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  246.            WAITWRITE
  247.            mov     al,0D1h                     ; AL = Enable speaker
  248.            out     dx,al                       ; Output: DSP Write Data or Command
  249.  
  250.            ; SETUP IRQ :
  251.            xor     ax,ax
  252.            mov     es,ax                       ; es to page 0 (Interrupt table)
  253.            mov     si,IRQ7*4                   ; si = position in interrupt table
  254.  
  255.            ; DISABLE IRQ (if it was enabled somehow)
  256.            in      al,021h
  257.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  258.            out     021h,al
  259.  
  260.            ; CHANGE POINTER IN INTERRUPT TABLE
  261.            mov     ax,es:[si]
  262.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  263.            mov     ax,OFFSET OWN_IRQ
  264.            mov     es:[si],ax                  ; set offset of new interrupt routine
  265.            mov     ax,es:[si+2]
  266.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  267.            mov     ax,cs
  268.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  269.  
  270.            ; CHANGE PIC MASK :
  271.            in      al,021h
  272.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  273.            out     021h,al
  274.  
  275.  
  276.            mov     cx,SAMPLEBUFFERLENGTH-1
  277. ;──────────────────────────────────────────────────────────────────────────────
  278. ; Setup DMA-controller :
  279. ;
  280. ; 1st  MASK DMA CHANNEL
  281. ;
  282.            mov     al,DMAchannel
  283.            add     al,4
  284.            out     WRITEMASK,al
  285. ;──────────────────────────────────────────────────────────────────────────────
  286. ; 2nd  CLEAR FLIPFLOP
  287. ;
  288.            out     CLEARFLIPFLOP,al
  289. ;──────────────────────────────────────────────────────────────────────────────
  290. ; 3rd  WRITE TRANSFER MODE
  291. ;
  292.            mov     al,WANTEDMODE
  293.            add     al,DMAchannel
  294.            out     WRITEMODE,al
  295. ;──────────────────────────────────────────────────────────────────────────────
  296. ; 4th  WRITE PAGE NUMBER
  297. ;
  298.            mov     al,[DMAbufferPage]
  299.            out     PAGE_CHN,al
  300. ;──────────────────────────────────────────────────────────────────────────────
  301. ; 5th  WRITE DMA BASEADDRESS
  302. ;
  303.            mov     ax,[DMABufferOFS]
  304.            out     BASE_CHN,al
  305.            mov     al,ah
  306.            out     BASE_CHN,al
  307. ;──────────────────────────────────────────────────────────────────────────────
  308. ; 6th  WRITE BASECOUNTER = SAMPLELENGTH-1
  309. ;
  310.            mov     al,cl
  311.            out     COUNT_CHN,al
  312.            mov     al,ch
  313.            out     COUNT_CHN,al
  314. ;──────────────────────────────────────────────────────────────────────────────
  315. ; 7th  DEMASK CHANNEL
  316. ;
  317.            mov     al,DMAchannel
  318.            out     WRITEMASK,al
  319.  
  320. ;──────────────────────────────────────────────────────────────────────────────
  321. ; Setup SoundBlaster :
  322. ;
  323. ; 1st  SET SAMPLERATE
  324. ;
  325.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  326.            WAITWRITE
  327.            mov     al,041h                     ;AL = Set DAC Samplerate
  328.            out     dx,al
  329.            WAITWRITE
  330.            mov     cx,RATE
  331.            mov     al,ch
  332.            out     dx,al
  333.            WAITWRITE
  334.            mov     al,cl
  335.            out     dx,al
  336.  
  337. ;──────────────────────────────────────────────────────────────────────────────
  338. ; 2nd  play stereo unsigned data (DSPC C6h 20h XX XXh)
  339. ;
  340.            WAITWRITE
  341.            mov     al,0C6h                     ;AL = DMA DAC 8bit autoinit
  342.            out     dx,al
  343.            WAITWRITE
  344.            mov     al,020h                     ;AL = stereo unsigned data
  345.            out     dx,al
  346.            mov     cx,SAMPLEBUFFERLENGTH
  347.            shr     cx,1                        ; after every half buffer report
  348.            dec     cx
  349.            WAITWRITE
  350.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  351.            out     dx,al
  352.            WAITWRITE
  353.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  354.            out     dx,al
  355.  
  356. ; TRANSFER STARTs.....NOW.... :)
  357.  
  358. waitloop:  mov     ah,01                       ;AH = Check for character function
  359.            int     016h                        ;   Interrupt: Keyboard
  360.            jz      waitloop                    ; wait for a key (sound in background)
  361.            xor     ah,ah                       ;Read character, flush keypress
  362.            int     016h                        ;   Interrupt: Keyboard
  363.            cmp     al,'p'                      ; check for pause key
  364.            je      pause                       ; ok
  365.            cmp     al,27
  366.            jne     waitloop
  367.            jmp     exit                        ; other key so stop playing
  368. pause:     ; NOW HALT PLAYING :
  369.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  370.            WAITWRITE
  371.            mov     al,0D0h
  372.            out     dx,al
  373.  
  374.            ; WAIT FOR ANY KEY
  375.            xor     ah,ah                       ;Read character, flush keypress
  376.            int     016h                        ;   Interrupt: Keyboard
  377.  
  378.            ; NOW CONTINUE PLAYING
  379.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  380.            WAITWRITE
  381.            mov     al,0d4h
  382.            out     dx,al
  383.  
  384.            jmp     waitloop
  385.  
  386. exit:      RESET_DSP
  387.  
  388.            ; RESTORE PIC MASK
  389.            in      al,021h
  390.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  391.            out     021h,al
  392.  
  393.            ; RESTORE IRQ :
  394.            xor     ax,ax
  395.            mov     es,ax                       ; es to page 0 (Interrupt table)
  396.            mov     si,IRQ7*4
  397.            mov     ax,[OLDInterruptOFS]
  398.            mov     es:[si],ax                  ; set old interrupt routine
  399.            mov     ax,[OLDInterruptSEG]
  400.            mov     es:[si+2],ax
  401.  
  402.            ; CLEAR KEYBUFFER
  403.            mov     ah,01
  404.            int     16h
  405.            jz      return2dos
  406.            xor     ah,ah                       ;Read character, flush keypress
  407.            int     016h                        ;   Interrupt: Keyboard
  408.  
  409.            ; TERMINATE EXE:
  410. return2dos:
  411.            mov     ax,04c00h
  412.            int     21h
  413.  
  414. ; display information if Soundblaster is not on this baseaddress
  415. RESET_ERROR:
  416.            mov     dx,offset sberror
  417.            mov     ah,9
  418.            int     21h                         ; text output
  419.            jmp     return2dos
  420.  
  421. ;──────────────────────────────────────────────────────────────────────────────
  422. ; Our own IRQ for detecting buffer half SB currently plays
  423. ; It's generated by the SoundBlaster hardware
  424. ;──────────────────────────────────────────────────────────────────────────────
  425. OWN_IRQ:   pusha
  426.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  427.            in      al,dx
  428.            mov     ax,@data
  429.            mov     ds,ax
  430.            mov     dx,offset txtpart0
  431.            cmp     [part],0
  432.            je      notpart1
  433.            mov     dx,offset txtpart1
  434. notpart1:  mov     ah,9
  435.            int     21h             ; text output
  436.            neg     [part]
  437.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  438.            mov     al,020h
  439.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  440.            popa
  441.            IRET
  442.  
  443. END     __start
  444.